#include "../include/LecteurPhraseSimple.h"

#include <stdlib.h>
#include <iostream>

using namespace std;

LecteurPhraseSimple::LecteurPhraseSimple(string nomFich) : m_ls(nomFich)
{
}

void LecteurPhraseSimple::analyse()
{
    programme();
    cout << "Syntaxe correcte." << endl;
}

void LecteurPhraseSimple::programme()
{
    // <programme> ::= debut <seqInst> fin FIN_FICHIER
    sauterSymCour("debut");
    seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
}

void LecteurPhraseSimple::seqInst()
{
    // <seqInst> ::= <inst> ; { <inst> ; }
    do
    {
        inst();
        sauterSymCour(";");
    }
    while(this->m_ls.getSymCour() == "<VARIABLE>" || this->m_ls.getSymCour() == "si" ||
            this->m_ls.getSymCour() == "tantque"    || this->m_ls.getSymCour() == "repeter" ||
            this->m_ls.getSymCour() == "lire"       || this->m_ls.getSymCour() == "ecrire" ||
            this->m_ls.getSymCour() == "pour");
    // tant que le symbole courant est un debut possible d'instruction...
}

void LecteurPhraseSimple::inst()
{
    // <inst> ::= <affectation> | <si> | <tantque> | <repeter>
    if(this->m_ls.getSymCour() == "si")
    {
        instSi();
    }
    else if(this->m_ls.getSymCour() == "tantque")
    {
        instTq();
    }
    else if(this->m_ls.getSymCour() == "repeter")
    {
        instRepeter();
    }
    else if(this->m_ls.getSymCour() == "lire")
    {
        instLire();
    }
    else if(this->m_ls.getSymCour() == "ecrire")
    {
        instEcrire();
    }
    else if(this->m_ls.getSymCour() == "pour")
    {
        instPour();
    }
    else
    {
        affectation();
    }
}

void LecteurPhraseSimple::instSi()
{
    if(this->m_ls.getSymCour() == "si")
    {
        sauterSymCour("si");
        sauterSymCour("(");
        expBool();
        sauterSymCour(")");
        seqInst();
        while(this->m_ls.getSymCour() == "sinonsi")
        {
            sauterSymCour("sinonsi");
            sauterSymCour("(");
            expBool();
            sauterSymCour(")");
            seqInst();
        }
        if(this->m_ls.getSymCour() == "sinon")
        {
            sauterSymCour("sinon");
            seqInst();
        }
        sauterSymCour("finsi");
    }
}

void LecteurPhraseSimple::instTq()
{
    if(this->m_ls.getSymCour() == "tantque")
    {
        sauterSymCour("tantque");
        sauterSymCour("(");
        expBool();
        sauterSymCour(")");
        seqInst();
        sauterSymCour("fintantque");
    }
}

void LecteurPhraseSimple::instRepeter()
{
    if(this->m_ls.getSymCour() == "repeter")
    {
        sauterSymCour("repeter");
        seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        expBool();
        sauterSymCour(")");
    }
}

void LecteurPhraseSimple::affectation()
{
    // <affectation> ::= <variable> = <expression>
    sauterSymCour("<VARIABLE>");
    sauterSymCour("=");
    expression();
}

void LecteurPhraseSimple::expression()
{
    // <terme> { <opAdd> <terme> }
    terme();
    while(this->m_ls.getSymCour() == "+" || this->m_ls.getSymCour() == "-")
    {
        opAdd();
        terme();
    }
}

void LecteurPhraseSimple::terme()
{
    // <expression> ::= <facteur> { <opMult> <facteur> }
    facteur();
    while(this->m_ls.getSymCour() == "*" || this->m_ls.getSymCour() == "/")
    {
        opMult();
        facteur();
    }
}

void LecteurPhraseSimple::facteur()
{
    // <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )
    if(this->m_ls.getSymCour() == "<VARIABLE>" || this->m_ls.getSymCour() == "<ENTIER>")
    {
        this->m_ls.suivant();
    }
    else if((this->m_ls.getSymCour() == "-") || (this->m_ls.getSymCour() == "non"))
    {
        opUnaire();
        expBool();
    }
    else if(this->m_ls.getSymCour() == "(")
    {
        this->m_ls.suivant();
        expBool();
        sauterSymCour(")");
    }
    else
    {
        erreur("<facteur>");
    }
}

void LecteurPhraseSimple::expEt() //permet de rendre expBool plus lisible
{
    // <expEt> ::= <relation> { <opBool> <relation> }
    relation();
    while(this->m_ls.getSymCour() == "et")
    {
        opBool();
        relation();
    }
}

void LecteurPhraseSimple::expBool()
{
    // <expBool> ::= <expEt> { <opBool> <expEt> }
    expEt();
    while(this->m_ls.getSymCour() == "ou")
    {
        opBool();
        expEt();
    }
}

void LecteurPhraseSimple::relation()
{
    // <relation> ::= <expression> { <opRel> <expression> }
    expression();
    while(this->m_ls.getSymCour() == "<"   || this->m_ls.getSymCour() == "<=" ||
            this->m_ls.getSymCour() == ">"   || this->m_ls.getSymCour() == ">=" ||
            this->m_ls.getSymCour() == "==" || this->m_ls.getSymCour() == "!=")
    {
        opRel();
        expression();
    }
}

void LecteurPhraseSimple::opAdd()
{
    // <opAdd>  ::= + | -
    if(this->m_ls.getSymCour() == "+" || this->m_ls.getSymCour() == "-")
    {
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opAdd>");
    }
}

void LecteurPhraseSimple::opMult()
{
    // <opMult>  ::= *  | /
    if(this->m_ls.getSymCour() == "*" || this->m_ls.getSymCour() == "/")
    {
        m_ls.suivant();
    }
    else
    {
        erreur("<opMult>");
    }
}

void LecteurPhraseSimple::opBool()
{
    // <opBool> ::=  "et" | "ou"
    if(this->m_ls.getSymCour() == "et" || this->m_ls.getSymCour() == "ou")
    {
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opBool>");
    }
}

void LecteurPhraseSimple::opRel()
{
    // <opRel>  ::= <  | <= | > | >= | == | !=
    if(this->m_ls.getSymCour() == "<"   || this->m_ls.getSymCour() == "<=" ||
            this->m_ls.getSymCour() == ">"   || this->m_ls.getSymCour() == ">=" ||
            this->m_ls.getSymCour() == "==" || this->m_ls.getSymCour() == "!=")
    {
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opRel>");
    }
}

void LecteurPhraseSimple::opUnaire()
{
    // <opUnaire> ::= - | non
    if(this->m_ls.getSymCour() == "-" || this->m_ls.getSymCour() == "non")
    {
        this->m_ls.suivant();
    }
    else
    {
        erreur("<opUnaire>");
    }
}

void LecteurPhraseSimple::testerSymCour(string ch)
{
    if(this->m_ls.getSymCour() != ch)
    {
        cout << endl << "-------- Erreur ligne " << this->m_ls.getLigne()
             << " - Colonne " << this->m_ls.getColonne() << endl << "   Attendu : "
             << ch << endl << "   Trouve  : " << this->m_ls.getSymCour() << endl
             << endl;
        exit(0);    // plus tard, on levera une exception
    }
}

void LecteurPhraseSimple::sauterSymCour(string ch)
{
    testerSymCour(ch);
    this->m_ls.suivant();
}

void LecteurPhraseSimple::erreur(string mess)
{
    cout << endl << "-------- Erreur ligne " << this->m_ls.getLigne() << " - Colonne "
         << this->m_ls.getColonne() << endl << "   Attendu : " << mess << endl
         << "   Trouve  : " << this->m_ls.getSymCour() << endl << endl;
    exit(0);    // plus tard, on levera une exception
}

void LecteurPhraseSimple::instLire()
{
    // <instLire> ::= lire ( <variable> )
    sauterSymCour("lire");
    sauterSymCour("(");
    if(this->m_ls.getSymCour() == "<VARIABLE>")
    {
        facteur();
        sauterSymCour(")");
    }
    else
    {
        erreur("<variable>");
    }
}

void LecteurPhraseSimple::instEcrire()
{
    // <instEcrire> ::= ecrire ( <expression> | <chaine> )
    if(this->m_ls.getSymCour() == "ecrire")
    {
        sauterSymCour("ecrire");
        sauterSymCour("(");
        if(this->m_ls.getSymCour() == "<CHAINE>")
        {
            sauterSymCour("<CHAINE>");
        }
        else
        {
            expression();
        }
        sauterSymCour(")");
    }
}

void LecteurPhraseSimple::instPour() //gerer les erreurs
{
    // <instPour> ::= pour (<affecation>; <expBool>; <affectation>;)
    if(this->m_ls.getSymCour() == "pour")
    {
        sauterSymCour("pour");
        sauterSymCour("(");
        affectation();
        sauterSymCour(";");
        expBool();
        sauterSymCour(";");
        affectation();
        sauterSymCour(")");
        seqInst();
        sauterSymCour("finpour");
    }
}
